package com.example.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;

import java.util.Collections;

/**
 * Redis 分布式锁实现
 * 如有疑问可参考 @see <a href="https://www.cnblogs.com/linjiqin/p/8003838.html">Redis分布式锁的正确实现方式</a>
 **/
@Service
public class RedisLock {
    private static final Long RELEASE_SUCCESS = 1L;
    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    // 当前设置 过期时间单位, EX  seconds,PX millisecond
    private static final String SET_WITH_EXIST_TIME = "EX";
    // if get(key) == value  return del(key)
    private static final String RELEASE_LOCK_SCRIPT =
            "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";


    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 加锁
     * 线程安全, 支重复
     * 针对redis单例实现分布式锁
     * @param lockKey 加锁键
     * @param clientId 客户端id(采用UUID)
     * @param second 锁过期时间
     */
    public boolean tryLock(String lockKey, String clientId, long second) {
        return redisTemplate.execute((RedisCallback<Boolean>) redisConnection ->{
            Jedis jedis = (Jedis) redisConnection.getNativeConnection();
            String result = jedis.set(lockKey, clientId, SET_IF_NOT_EXIST, SET_WITH_EXIST_TIME, second);

            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
            return false;

        });
    }

    public boolean releaseLock(String lockkey, String clientId) {
        return redisTemplate.execute((RedisCallback<Boolean>)redisCollection ->{
            Jedis jedis = (Jedis) redisCollection.getNativeConnection();
            Object result = jedis.eval(RELEASE_LOCK_SCRIPT, Collections.singletonList(lockkey),
                    Collections.singletonList(clientId));

            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
            return false;
        } );
    }
}
